package net.takela.common.security.filter;


import net.takela.common.spring.exception.DataException;
import net.takela.common.spring.filter.HttpRequestCachedServlet;
import jakarta.annotation.PostConstruct;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.filter.OncePerRequestFilter;

import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;

public class RequestParamSignAuthFilter extends OncePerRequestFilter {

    private boolean enabled = true;
    private String signHeader;

    private String key;
    private String timestampHeader;

    private List<String> checkUrls;
    private List<String> uncheckUrls;
    private List<AntPathRequestMatcher> checkURLMatchers;
    private List<AntPathRequestMatcher> uncheckURLMatchers;
    private AntPathMatcher antPathMatcher = new AntPathMatcher();
    /**
     * 
     */
    public boolean isEnabled() {
        return enabled;
    }
    /**
     * 
     */
    public void setEnabled(boolean enabled) {
        this.enabled = enabled;
    }
    /**
     * 
     */
    public String getSignHeader() {
        return signHeader;
    }
    /**
     * 
     */
    public void setSignHeader(String signHeader) {
        this.signHeader = signHeader;
    }
    /**
     * 
     */
    public String getKey() {
        return key;
    }
    /**
     * 
     */
    public void setKey(String key) {
        this.key = key;
    }
    /**
     * 
     */
    public String getTimestampHeader() {
        return timestampHeader;
    }
    /**
     * 
     */
    public void setTimestampHeader(String timestampHeader) {
        this.timestampHeader = timestampHeader;
    }
    /**
     * 
     */
    public List<String> getCheckUrls() {
        return checkUrls;
    }
    /**
     * 
     */
    public void setCheckUrls(List<String> checkUrls) {
        this.checkUrls = checkUrls;
    }
    /**
     * 
     */
    public List<String> getUncheckUrls() {
        return uncheckUrls;
    }
    /**
     * 
     */
    public void setUncheckUrls(List<String> uncheckUrls) {
        this.uncheckUrls = uncheckUrls;
    }

    
    /**
     * 
     */
    @PostConstruct
    public void init(){
        checkURLMatchers = new ArrayList<>();
        if (checkUrls != null){
            for (String url : checkUrls){
                checkURLMatchers.add( new AntPathRequestMatcher(url));
            }
        }
        uncheckURLMatchers = new ArrayList<>();
        if (uncheckUrls != null){
            for (String url : uncheckUrls){
                uncheckURLMatchers.add( new AntPathRequestMatcher(url));
            }
        }
    }
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException, DataException {
        //没有启用
        if (!enabled){
            filterChain.doFilter(request, response);
            return;
        }
        HttpRequestCachedServlet cachedServletRequest = new HttpRequestCachedServlet((HttpServletRequest)request);
//        boolean isUncheck = uncheckURLMatchers.stream().anyMatch((m)->{ return m.matches(request)?true:false;});
        boolean isUncheck = uncheckUrls.stream().anyMatch( url -> antPathMatcher.match(url, request.getRequestURI()));
        if (isUncheck){
            filterChain.doFilter(cachedServletRequest, response);
            return;
        }

//        boolean f = checkURLMatchers.stream().anyMatch((m)->{ return m.matches(request)?true:false;});
        boolean f = checkUrls.stream().anyMatch( url -> antPathMatcher.match(url, request.getRequestURI()));
        if (!f){
            filterChain.doFilter(request, response);
            return;
        }
        String t = request.getHeader(timestampHeader);
        String sign = request.getHeader(signHeader);
        if (StringUtils.isEmpty(t)  || StringUtils.isEmpty(sign)){
            throw new AuthenticationException("miss some header"){};
        }
        String q = cachedServletRequest.getQueryString();
        String b = null;
        if (cachedServletRequest.getContentType() != null && cachedServletRequest.getContentType().toLowerCase().contains("application/json")){
            b = getBody( cachedServletRequest.getInputStream());
        }
        String s2 = key + (StringUtils.isEmpty(q)?"":q) + (StringUtils.isEmpty(b)?"":b)+t;

        String sign2 = DigestUtils.sha256Hex(s2);
        if (sign != null && sign.equalsIgnoreCase(sign2) ){
            filterChain.doFilter(cachedServletRequest, response);
            return;
        }
        throw new AuthenticationException("sign error"){};
    }

    private String getBody(InputStream inputStream){
        StringBuilder sb = new StringBuilder();
        BufferedReader reader = null;
        try
        {
            reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
            char[] buffer = new char[1024];
            int bytesRead = -1;
            while ( (bytesRead = reader.read(buffer) ) > 0 )
            {
                sb.append(buffer, 0, bytesRead);
            }
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }
//        finally
//        {
//            if (reader != null)
//            {
//                try
//                {
//                    reader.close();
//                }
//                catch (IOException e)
//                {
//                    e.printStackTrace();
//                }
//            }
//        }
        return sb.toString();
    }
}
